<?php
/*
 *  Copyright (c) 2015 The UCLBRT project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a Guangzhou Changlian Information Technology Co.,Ltd license
 *  that can be found in the LICENSE file in the root of the web site.
 *
 *   http://www.uclbrt.com
 *
 *  An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */
namespace Org\Util;

class RedisHelper {
    private $defaultServerIp = '127.0.0.1';
    private $defaultServerPort = 6379;
    private $db = 'db';
    private $redis;
    private $connected = false;
    //查询删除时的条件
    private $options = array();
    private $valueStatus = array('GT', 'EGT', 'LT', 'ELT', 'EQ', 'NEQ', 'LIKE', 'IN');

    /**
     * 构造函数
     * 传入ip 端口 连接时间(秒)进行初始化,连接redis
     */
    function __construct($serverIp, $serverPort, $db, $timeout = 0) {

        //初始化IP 如不设置 默认是本机 127.0.0.1
        if (!$serverIp) {
            if (C('REDIS_SERVERIP')) {
                $serverIp = C('REDIS_SERVERIP');
            } else {
                $serverIp = $this->defaultServerIp;
            }
        }

        //初始化端口 如不设置 默认是 6379端口
        if (!$serverPort) {
            if (C('REDIS_SERVERPORT')) {
                $serverPort = C('REDIS_SERVERPORT');
            } else {
                $serverPort = $this->defaultServerPort;
            }
        }

        //初始化库名 如不设置 默认是 db
        if (!$db) {
            if (C('REDIS_DB')) {
                $this->db = C('REDIS_DB');
            }
        } else {
            $this->db = $db;
        }

        $this->redis = new \Redis();
        $connectResult = $this->redis->connect($serverIp, $serverPort, $timeout);

        $redisPassword = C('REDIS_AUTH_PASSWORD');
        $redisAuthResult = $this->redis->auth($redisPassword);

        if ($connectResult && $redisAuthResult) {
            $this->connected = true;
        }
    }

    /**
     * 切换redis db
     * @modify trainyao 2015-11-27 创建
     * @return boolean 切换数据库结果
     */
    public function db($dbIndex) {
        if (!$this->connected) {
            return $this;
        }

        $result = $this->redis->select($dbIndex);
        if (empty($result)) {
            $this->connected = false;
        }
        return $this;
    }

    /**
     * 查询是否还存在该key
     * @modify AmosZhong 2015-11-26
     * @param  string $id     
     * @return boolean
     */
    public function exists($id) {
        $tableName = $this->options['table'];
        if (!$tableName) {
            return false;
        }
        if (!$this->connected) {
            return false;
        }

        $key = $this->db . ':' . $tableName . ':' . $id;

        return $this->redis->exists($key);
    }

    /**
     * 根据传入的id取多个字段值
     * @modify AmosZhong 2015-11-26
     * @param  string $id     
     * @return mixin 数组或者false
     */
    public function hMGet($id) {
        $tableName = $this->options['table'];
        if (!$tableName) {
            return false;
        }
        if (!$this->connected) {
            return false;
        }

        $key = $this->db . ':' . $tableName . ':' . $id;

        //返回所有带id的键名
        $where = $this->options['where'];
        $field = $this->options['field'];

        //没条件的查询,返回所有带id的条目
        if ($field) {
            $result = $this->redis->hMGet($key, $field);
            unset($this->options['field']); //用完field清空
        } else {
            $result = $this->redis->hGetAll($key);
        }

        return $result;
    }

    /**
     * 根据传入的id设置hash类型的value或者数组value
     * @modify AmosZhong 2015-11-26
     * @param  string $id     
     * @param  array $value  
     * @param  timestamp $expiry 有效期时间戳 unix
     * @return mixin 有可能是integer或者false         
     */
    public function hMSet($id, $value, $expiry = 0) {
        //如果参数不是数组格式
        if (!is_array($value)) {
            return false;
        }
        $tableName = $this->options['table'];
        if (!$tableName) {
            return false;
        }
        if (!$this->connected) {
            return false;
        }
        $id = $this->db . ':' . $tableName . ':' . $id; //拼接真正的key

        if (!$this->redis->hMset($id, $value)) {
            return false;
        }

        //如果插入成功
        if ($expiry != 0) {
            //如果要求设置过期时间
            $this->redis->EXPIREAT($id, $expiry);
        }
        return $id;
    }

    /**
     * 仿tp table方法,暂时只支持key=>value数组参数传入,支持字符串参数传入
     * @param string $tableName
     */
    public function table($tableName) {
        if (!$this->connected) {
            return $this;
        }
        $this->options['table'] = $tableName;
        return $this;
    }

    /**
     * 插入一条数据进表,暂时只支持key=>value数组的参数传入
     * @param array $values
     */
    public function add($values, $databaseId = '', $expireTime = 0) {
        //如果参数不是数组格式
        if (!is_array($values)) {
            return false;
        }
        $tableName = $this->options['table'];
        if (!$tableName) {
            return false;
        }
        if (!$this->connected) {
            return false;
        }
        $id = $this->getId(); //获取唯一值
        $id = $this->db . '_' . $tableName . '_' . $databaseId . '_' . $id; //拼接真正的key

        if ($this->redis->hMset($id, $values)) {
            //如果插入成功
            if ($expireTime != 0) {
                //如果要求设置过期时间
                if ($this->redis->EXPIREAT($id, $expireTime)) {
                    return $id;
                }
            }
            //不要求设置过期时间
            return $id;
        }
        return false;
    }

    /**
     * 更新值
     * @modify traiynao 2015-12-07  创建
     * @param  array   $values       值数组
     * @param  string   $key         redis key
     * @param  integer  $expireTime  超时时间
     * @return boolean 修改结果
     */
    public function update($values, $key, $expireTime = 0) {
        //如果参数不是数组格式
        if (!is_array($values)) {
            return false;
        }
        $tableName = $this->options['table'];
        if (!$tableName) {
            return false;
        }
        if (!$this->connected) {
            return false;
        }

        if ($this->redis->hMset($key, $values)) {
            //如果插入成功
            if ($expireTime != 0) {
                //如果要求设置过期时间
                $this->redis->EXPIREAT($key, $expireTime);
            }
            return true;
        }
        return false;
    }

    /**
     * 取出表中数据
     * @param string $table
     * @param string $id
     * @param array $fields
     */
    public function select($id = '') {
        $tableName = $this->options['table'];
        if (!$tableName) {
            return false;
        }
        if (!$this->connected) {
            return false;
        }

        //如果$id有传入,添加profix
        $profix = $id === '' ? '' : ('_' . $id);
        $keys = $this->redis->keys($this->db . '_' . $tableName . $profix . '*');

        //返回所有带id的键名
        $where = $this->options['where'];
        $field = $this->options['field'];
        $result = array();

        //没条件的查询,返回所有带id的条目
        if ($field) {
            foreach ($keys as $keyName) {
                array_push($result, $this->redis->hMGet($keyName, $field));
            }
            unset($this->options['field']); //用完field清空
        }
        foreach ($keys as $keyName) {
            array_push($result, $this->redis->hGetAll($keyName));
        }

        // //筛选数据
        // $result = $this->_whereFilter($result);

        return $result;
    }

    /**
     * 根据id值删除条目
     */
    public function delete($id = '') {
        if ($id == '') {
            return false;
        }
        $tableName = $this->options['table'];
        if (!$tableName) {
            return false;
        }
        if (!$this->connected) {
            return false;
        }

        $key = $this->redis->keys($this->db . '_' . $tableName . '_' . $id . '*');
        if (empty($key)) {
            return false;
        }

        $deleteResult = $this->redis->EXPIRE($key[0], 0);

        if (!$deleteResult) {
            return false;
        }
        return true;
    }

    /**
     * 设置键值
     * @param type $key
     * @param type $value
     */
    public function set($key, $value) {
        if ($this->connected) {
            return $this->redis->SET($key, $value);
        }
        return false;
    }

    /**
     * 返回键值
     * @param type $key
     */
    public function get($key) {
        return $this->redis->GET($key);
    }

    /**
     * 仿tp where方法,暂时只支持key=>value数组参数传入,支持字符串参数传入
     * @param array $optionArray
     */
    public function where($optionArray) {
        if (!is_array($optionArray)) {
            return $this;
        }
        if (!$this->connected) {
            return $this;
        }
        $this->options['where'] = $optionArray;
        return $this;
    }

    /**
     * 仿tp field方法,暂时只支持key=>value数组参数传入,支持字符串参数传入
     * @param array $optionArray
     */
    public function field($optionArray) {
        if (!is_array($optionArray)) {
            return $this;
        }
        if (!$this->connected) {
            return $this;
        }
        $this->options['field'] = $optionArray;
        return $this;
    }

    /**
     * 根据where数组过滤result数据
     * @param array $result
     */
    private function _whereFilter($result) {
        $where = $this->options['where'];
        if (!$where) {
            return $result;
        }
        $result = array();

        foreach ($where as $whereKey => $whereValue) {
            $result = $this->_optionFilter($result, $whereKey, $whereValue);
        }
        //用完了清除
        unset($this->options['where']);
        return $result;
    }

    /**
     * 根据key=>value条件,筛选数组
     * @param array $result
     * @param string $optionKey
     * @param string/array $optionValue
     */
    private function _optionFilter($result, $optionKey, $optionValue) {
        if (!$result) {
            return false;
        }
        $newResult = array();

        //$optionKey $optionValue筛选数据
        foreach ($result as $resultItem) {
            //筛选所有的条目
            if (array_key_exists($optionKey, $resultItem)) {
                //存在该键
                if (is_array($optionValue) && in_array($optionValue[0], $this->valueStatus)) {
                    //条件是个判断式 且在规定的值内
                    $optionPass = false;
                    switch ($optionValue[0]) {
                        case 'GT':
                            $optionPass = ($resultItem[$optionKey] > $optionValue[1]);
                            break;
                        case 'EGT':
                            $optionPass = ($resultItem[$optionKey] >= $optionValue[1]);
                            break;
                        case 'LT':
                            $optionPass = ($resultItem[$optionKey] < $optionValue[1]);
                            break;
                        case 'ELT':
                            $optionPass = ($resultItem[$optionKey] <= $optionValue[1]);
                            break;
                        case 'EQ':
                            $optionPass = ($resultItem[$optionKey] == $optionValue[1]);
                            break;
                        case 'NEQ':
                            $optionPass = ($resultItem[$optionKey] != $optionValue[1]);
                            break;
                        case 'LIKE':
                            $optionPass = preg_match('/' . $resultItem[$optionKey] . '/', $optionValue[1]);
                            break;
                        case 'IN':
                            $optionPass = in_array($resultItem[$optionKey], $optionValue[1]);
                            break;
                    }
                    if ($optionPass) {
                        array_push($newResult, $resultItem);
                    }
                } else {
                    //条件不是判断式是等值
                    if ($optionValue == $resultItem[$optionKey]) {
                        //如果相等
                        array_push($newResult, $resultItem);
                    }
                }
            }
        }

        return $newResult;
    }

    /**
     * 生成唯一值
     * @param type $key
     */
    private function getId() {
        return time() . $this->randCode();
    }

    /**
     * 生成随机码
     * @param type $key
     */
    private function randCode($length = 6, $type = 1) {
        $arr = array(1 => "0123456789",
            2 => "abcdefghijklmnopqrstuvwxyz",
            3 => "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
            4 => "~@#$%^&*(){}[]|");
        if ($type == 0) {
            array_pop($arr);
            $string = implode("", $arr);
        } elseif ($type == "-1") {
            $string = implode("", $arr);
        } else {
            $string = $arr[$type];
        }
        $count = strlen($string) - 1;
        $code = '';
        for ($i = 0; $i < $length; $i++) {
            $code .= $string[rand(0, $count)];
        }
        return $code;
    }
}
